package com.jiaying.sync;

import com.mongodb.*;
import com.mongodb.client.*;
import com.mongodb.client.result.InsertOneResult;
import com.mongodb.connection.ClusterDescription;
import com.mongodb.connection.ServerDescription;
import org.bson.Document;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

// 添加导入配置类
import com.jiaying.sync.config.MongoConfig;

/**
 * MongoDB集群测试类
 * 测试主从切换、故障转移和读写分离功能
 */
public class MongoClusterTest {
    private MongoClient mongoClient;
    private MongoConfig config;
    
    public static void main(String[] args) {
        MongoClusterTest tester = new MongoClusterTest();
        
        try {
            tester.initializeConnection();
            tester.printClusterStatus();
            tester.testConnection();
            tester.testReadWriteSeparation();
            tester.testConcurrentAccess();
            tester.simulateFailover();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            tester.closeConnection();
        }
    }
    
    /**
     * 初始化MongoDB连接
     */
    public void initializeConnection() {
        try {
            // 初始化配置
            config = new MongoConfig();
            
            // 构建连接字符串
            String connectionString = "mongodb://" + config.getClusterHosts();
            
            // 配置MongoClient设置
            MongoClientSettings settings = MongoClientSettings.builder()
                    .applyConnectionString(new ConnectionString(connectionString))
                    .applyToClusterSettings(builder -> 
                        builder.serverSelectionTimeout(config.getServerSelectionTimeoutMs(), TimeUnit.MILLISECONDS))
                    .applyToSocketSettings(builder -> 
                        builder.connectTimeout(config.getConnectionTimeoutMs(), TimeUnit.MILLISECONDS)
                               .readTimeout(config.getSocketTimeoutMs(), TimeUnit.MILLISECONDS))
                    .applyToConnectionPoolSettings(builder ->
                        builder.maxSize(config.getMaxConnectionPoolSize())
                               .minSize(config.getMinConnectionPoolSize())
                               .maxWaitTime(10, TimeUnit.SECONDS))
                    .build();
            
            // 创建MongoClient实例
            mongoClient = MongoClients.create(settings);
            
            System.out.println("成功连接到MongoDB集群: " + config.getClusterHosts());
        } catch (Exception e) {
            System.err.println("连接MongoDB集群失败: " + e.getMessage());
            throw e;
        }
    }
    
    /**
     * 打印集群状态
     */
    public void printClusterStatus() {
        try {
            ClusterDescription clusterDescription = mongoClient.getClusterDescription();
            System.out.println("=== 集群状态 ===");
            System.out.println("集群类型: " + clusterDescription.getType());
            
            List<ServerDescription> serverDescriptions = clusterDescription.getServerDescriptions();
            for (ServerDescription serverDesc : serverDescriptions) {
                System.out.println("服务器地址: " + serverDesc.getAddress());
                System.out.println("服务器类型: " + serverDesc.getType());
                System.out.println("状态: " + serverDesc.getState());
                System.out.println("是否为主节点: " + serverDesc.isPrimary());
                System.out.println("响应延迟: " + serverDesc.getRoundTripTimeNanos() + " ns");
                System.out.println("---");
            }
        } catch (Exception e) {
            System.err.println("获取集群状态失败: " + e.getMessage());
        }
    }
    
    /**
     * 测试基本连接和操作
     */
    public void testConnection() {
        try {
            MongoDatabase database = mongoClient.getDatabase(config.getDatabaseName());
            MongoCollection<Document> collection = database.getCollection(config.getCollectionName());
            
            // 插入测试文档
            Document testDoc = new Document("test", "connection")
                    .append("timestamp", System.currentTimeMillis())
                    .append("description", "基础连接测试");
            
            InsertOneResult result = collection.insertOne(testDoc);
            System.out.println("基础连接测试 - 插入文档成功，ID: " + result.getInsertedId());
            
            // 查询文档
            Document foundDoc = collection.find(new Document("test", "connection")).first();
            if (foundDoc != null) {
                System.out.println("基础连接测试 - 查询文档成功: " + foundDoc.toJson());
            }
            
            System.out.println("基础连接测试完成");
        } catch (Exception e) {
            System.err.println("基础连接测试失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    /**
     * 测试读写分离
     */
    public void testReadWriteSeparation() {
        try {
            System.out.println("\n=== 读写分离测试 ===");
            MongoDatabase database = mongoClient.getDatabase(config.getDatabaseName());
            MongoCollection<Document> collection = database.getCollection(config.getCollectionName());
            
            // 写操作 - 默认使用主节点
            System.out.println("1. 执行写操作（主节点）...");
            Document writeDoc = new Document("operation", "write")
                    .append("type", "primary")
                    .append("timestamp", System.currentTimeMillis());
            
            InsertOneResult writeResult = collection.insertOne(writeDoc);
            System.out.println("   写入成功，文档ID: " + writeResult.getInsertedId());
            
            // 强制从主节点读取
            System.out.println("2. 从主节点读取...");
            MongoCollection<Document> primaryReadCollection = collection.withReadPreference(ReadPreference.primary());
            Document primaryDoc = primaryReadCollection.find(new Document("operation", "write")).first();
            if (primaryDoc != null) {
                System.out.println("   主节点读取成功: " + primaryDoc.toJson());
            }
            
            // 从从节点读取（如果可用）
            System.out.println("3. 从从节点读取...");
            MongoCollection<Document> secondaryReadCollection = collection.withReadPreference(ReadPreference.secondaryPreferred());
            Document secondaryDoc = secondaryReadCollection.find(new Document("operation", "write")).first();
            if (secondaryDoc != null) {
                System.out.println("   从节点读取成功: " + secondaryDoc.toJson());
            } else {
                System.out.println("   从节点暂不可用，使用最近的节点");
            }
            
            System.out.println("读写分离测试完成\n");
        } catch (Exception e) {
            System.err.println("读写分离测试失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    /**
     * 测试并发访问
     */
    public void testConcurrentAccess() {
        try {
            System.out.println("=== 并发访问测试 ===");
            MongoDatabase database = mongoClient.getDatabase(config.getDatabaseName());
            MongoCollection<Document> collection = database.getCollection(config.getCollectionName());
            
            ExecutorService executor = Executors.newFixedThreadPool(10);
            List<Runnable> tasks = new ArrayList<>();
            
            // 创建10个并发任务
            for (int i = 0; i < 10; i++) {
                final int taskId = i;
                Runnable task = () -> {
                    try {
                        Document doc = new Document("taskId", taskId)
                                .append("thread", Thread.currentThread().getName())
                                .append("timestamp", System.currentTimeMillis());
                        
                        InsertOneResult result = collection.insertOne(doc);
                        System.out.println("任务 " + taskId + " 插入成功，ID: " + result.getInsertedId());
                        
                        // 随机延迟
                        Thread.sleep((long) (Math.random() * 100));
                    } catch (Exception e) {
                        System.err.println("任务 " + taskId + " 失败: " + e.getMessage());
                    }
                };
                tasks.add(task);
            }
            
            // 提交所有任务
            tasks.forEach(executor::submit);
            
            // 关闭线程池并等待完成
            executor.shutdown();
            if (executor.awaitTermination(30, TimeUnit.SECONDS)) {
                System.out.println("所有并发任务完成");
            } else {
                System.out.println("任务超时");
                executor.shutdownNow();
            }
            
            // 验证插入的数据
            long count = collection.countDocuments();
            System.out.println("集合中文档总数: " + count);
            System.out.println("并发访问测试完成\n");
        } catch (Exception e) {
            System.err.println("并发访问测试失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    /**
     * 模拟故障转移测试
     */
    public void simulateFailover() {
        try {
            System.out.println("=== 故障转移模拟测试 ===");
            MongoDatabase database = mongoClient.getDatabase(config.getDatabaseName());
            MongoCollection<Document> collection = database.getCollection(config.getCollectionName());
            
            System.out.println("开始连续读写操作，观察故障转移行为...");
            
            for (int i = 1; i <= 20; i++) {
                try {
                    // 写入数据
                    Document doc = new Document("sequence", i)
                            .append("phase", "failover_test")
                            .append("timestamp", System.currentTimeMillis());
                    
                    InsertOneResult result = collection.insertOne(doc);
                    System.out.println("第 " + i + " 次写入成功");
                    
                    // 读取数据验证
                    Document latestDoc = collection.find()
                            .sort(new Document("timestamp", -1))
                            .limit(1)
                            .first();
                    
                    if (latestDoc != null) {
                        System.out.println("  验证读取: " + latestDoc.getInteger("sequence"));
                    }
                    
                    // 打印当前集群状态
                    if (i % 5 == 0) {
                        printClusterStatus();
                    }
                    
                    // 短暂延迟
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                } catch (Exception e) {
                    System.err.println("第 " + i + " 次操作失败: " + e.getMessage());
                    // 继续下一次尝试
                }
            }
            
            System.out.println("故障转移模拟测试完成\n");
        } catch (Exception e) {
            System.err.println("故障转移模拟测试异常: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    /**
     * 关闭连接
     */
    public void closeConnection() {
        if (mongoClient != null) {
            try {
                mongoClient.close();
                System.out.println("MongoDB连接已安全关闭");
            } catch (Exception e) {
                System.err.println("关闭连接时发生错误: " + e.getMessage());
            }
        }
    }
}